#ifndef TIME_MARCHING_LOOP_HPP
#define TIME_MARCHING_LOOP_HPP

#include "solverBase.hpp"
#include "sweSolver.hpp"
#include "sweTiming.hpp"
#include "timer.hpp"
#include "blockwiseIntegrated.hpp"

namespace hms
{

namespace marchingLoop {

template<typename SolverType, typename Exporter>
void basic(
	SolverBase<SolverType>& solver, Time& time, Exporter&& exportResults
){
	log::logger& logger { solver.logger() };

	if ( !time.useExplicitFirstStep() && !time.atEnd() ){
		time.step( solver.calculateTimeStep() );
		// setTiming(m_execTimings, LoopStep::timestep, t);
		logger.debug( "Time step length: {:.5f}s", time.step() );
	}

	while ( !time.atEnd() ){
		if( time.atWriteTime() ){
			time.print(logger);
			exportResults();
		}
		solver.advance( time );
	}
	time.print(logger);
	exportResults();

	printExecTimings( solver.derived() );
}

}


template<typename SolverType, typename Exporter>
void timeMarchingLoop(
	SolverBase<SolverType>& solver, Time& time, Exporter&& exportResults
){
	auto run = [&](){

		#define TIME_LOOP_ARGS \
			solver.derived(), time, std::forward<Exporter>(exportResults)

		using MeshType = typename SolverType::MeshType;
		if constexpr (
			std::is_same_v<SWESolver<MeshType>, SolverType> 
			&& SolverType::isOrthogonal
		){
			if ( solver.settings().useIntegratedBlockLoop ){
				swe::integratedTimeLoop(TIME_LOOP_ARGS);
				return;
			}
		}
		marchingLoop::basic(TIME_LOOP_ARGS);
		#undef TIME_LOOP_ARGS

	};

	log::logger& logger { solver.logger() };
	/* measurement timer */
	Timer t;
	run();
	double elapsed { t.elapsed() };
	logger.info( "Computation time: {:.3f}s", elapsed );
}

} // namespace hms


#endif